using System;
using System.Linq;
using System.Collections.Generic;
using System.Text;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Rhino.Mocks;
using Lincolnwood.Foundation.Scheduling;

namespace Lincolnwood.Foundation.Scheduling.Tests
{
    [TestClass]
    public class MonthlyRecurrencePatternTests
    {
        private MockRepository mockMaker;

        [TestInitialize]
        public void Init()
        {
            mockMaker = new MockRepository();
        }

        [TestMethod]
        public void Expand_WithEventDurationShorterThanRecurrenceInterval_AndAlignedExpandWindow_Expands()
        {
            DateTime testStart = DateTime.Parse("1/1/2000 1:00 pm");
            DateTime testEnd = DateTime.Parse("1/1/2000 2:00 pm");
            string testDesc = "Test Recurring Event";
            string testName = "Test Name";

            DateTime testRecurrenceEnd = DateTime.Parse("10/9/2000");

            RecurrableEventDefinition mockEvent = mockMaker.StrictMock<RecurrableEventDefinition>();
            Expect.Call(mockEvent.Name).Return(testName).Repeat.AtLeastOnce();
            Expect.Call(mockEvent.Description).Return(testDesc).Repeat.AtLeastOnce();
            Expect.Call(mockEvent.StartTime).Return(testStart).Repeat.AtLeastOnce();
            Expect.Call(mockEvent.EndTime).Return(testEnd).Repeat.AtLeastOnce();

            mockEvent.Replay();

            // expand the occurances
            MonthlyRecurrencePattern testPattern = new MonthlyRecurrencePattern(testRecurrenceEnd, 1);

            DateTime expandWindowBegin = DateTime.Parse("1/1/2000");
            DateTime expandWindowEnd = DateTime.Parse("11/9/2000");

            // make sure that the recurrence got computed correctly
            var expandedEvents = testPattern.Expand(mockEvent, expandWindowBegin, expandWindowEnd);

            Assert.AreEqual(10, expandedEvents.Count);

            for (int i = 1; i <= 9; i++)
            {
                int begin = i;
                int end = i;
                int index = i - 1;
                Assert.AreEqual(DateTime.Parse(String.Format("{0}/1/2000 1:00 pm", begin)), expandedEvents[index].StartTime);
                Assert.AreEqual(DateTime.Parse(String.Format("{0}/1/2000 2:00 pm", end)), expandedEvents[index].EndTime);
            }
        }

        [TestMethod]
        public void Expand_WithEventDurationLongerThanRecurrenceInterval_AndAlignedExpandWindow_Expands()
        {
            DateTime testStart = DateTime.Parse("1/1/2000 1:00 pm");
            DateTime testEnd = DateTime.Parse("2/3/2000 2:00 pm");
            string testDesc = "Test Recurring Event";
            string testName = "Test Name";

            DateTime testRecurrenceEnd = DateTime.Parse("11/9/2000");

            RecurrableEventDefinition mockEvent = mockMaker.StrictMock<RecurrableEventDefinition>();
            Expect.Call(mockEvent.Name).Return(testName).Repeat.AtLeastOnce();
            Expect.Call(mockEvent.Description).Return(testDesc).Repeat.AtLeastOnce();
            Expect.Call(mockEvent.StartTime).Return(testStart).Repeat.AtLeastOnce();
            Expect.Call(mockEvent.EndTime).Return(testEnd).Repeat.AtLeastOnce();

            mockEvent.Replay();

            // expand the occurances
            MonthlyRecurrencePattern testPattern = new MonthlyRecurrencePattern(testRecurrenceEnd, 1);

            DateTime expandWindowBegin = DateTime.Parse("1/1/2000");
            DateTime expandWindowEnd = DateTime.Parse("12/10/2000");

            // make sure that the recurrence got computed correctly
            var expandedEvents = testPattern.Expand(mockEvent, expandWindowBegin, expandWindowEnd);

            Assert.AreEqual(11, expandedEvents.Count);

        }

        [TestMethod]
        public void Expand_WithEventDurationLongerThanRecurrenceInterval_AndLargerExpandWindow_Expands()
        {
            DateTime testStart = DateTime.Parse("1/1/2000 1:00 pm");
            DateTime testEnd = DateTime.Parse("2/3/2000 2:00 pm");
            string testDesc = "Test Recurring Event";
            string testName = "Test Name";

            DateTime testRecurrenceEnd = DateTime.Parse("11/9/2000");

            RecurrableEventDefinition mockEvent = mockMaker.StrictMock<RecurrableEventDefinition>();
            Expect.Call(mockEvent.Name).Return(testName).Repeat.AtLeastOnce();
            Expect.Call(mockEvent.Description).Return(testDesc).Repeat.AtLeastOnce();
            Expect.Call(mockEvent.StartTime).Return(testStart).Repeat.AtLeastOnce();
            Expect.Call(mockEvent.EndTime).Return(testEnd).Repeat.AtLeastOnce();

            mockEvent.Replay();

            // expand the occurances
            MonthlyRecurrencePattern testPattern = new MonthlyRecurrencePattern(testRecurrenceEnd, 1);

            DateTime expandWindowBegin = DateTime.Parse("1/1/1900");
            DateTime expandWindowEnd = DateTime.Parse("12/10/2005");

            // make sure that the recurrence got computed correctly
            var expandedEvents = testPattern.Expand(mockEvent, expandWindowBegin, expandWindowEnd);

            Assert.AreEqual(11, expandedEvents.Count);

        }

        [TestMethod]
        public void Expand_WithEventDurationLongerThanRecurrenceInterval_AndExpandWindowThatStartsAfterTheEventStart_Expands()
        {
            DateTime testStart = DateTime.Parse("1/1/2000 1:00 pm");
            DateTime testEnd = DateTime.Parse("2/3/2000 2:00 pm");
            string testDesc = "Test Recurring Event";
            string testName = "Test Name";

            DateTime testRecurrenceEnd = DateTime.Parse("11/9/2000");

            RecurrableEventDefinition mockEvent = mockMaker.StrictMock<RecurrableEventDefinition>();
            Expect.Call(mockEvent.Name).Return(testName).Repeat.AtLeastOnce();
            Expect.Call(mockEvent.Description).Return(testDesc).Repeat.AtLeastOnce();
            Expect.Call(mockEvent.StartTime).Return(testStart).Repeat.AtLeastOnce();
            Expect.Call(mockEvent.EndTime).Return(testEnd).Repeat.AtLeastOnce();

            mockEvent.Replay();

            // expand the occurances
            MonthlyRecurrencePattern testPattern = new MonthlyRecurrencePattern(testRecurrenceEnd, 1);

            DateTime expandWindowBegin = DateTime.Parse("4/1/2000");
            DateTime expandWindowEnd = DateTime.Parse("12/10/2005");

            // make sure that the recurrence got computed correctly
            var expandedEvents = testPattern.Expand(mockEvent, expandWindowBegin, expandWindowEnd);

            Assert.AreEqual(8, expandedEvents.Count);
        }

        [TestMethod]
        public void Expand_WithEventDurationLongerThanRecurrenceInterval_AndExpandWindowThatStartsAndEndsAfterTheEventRecurrenceEnd_DoesNotExpand()
        {
            DateTime testStart = DateTime.Parse("1/1/2000 1:00 pm");
            DateTime testEnd = DateTime.Parse("2/3/2000 2:00 pm");
            string testDesc = "Test Recurring Event";
            string testName = "Test Name";

            DateTime testRecurrenceEnd = DateTime.Parse("11/9/2000");

            RecurrableEventDefinition mockEvent = mockMaker.StrictMock<RecurrableEventDefinition>();
            Expect.Call(mockEvent.Name).Return(testName).Repeat.AtLeastOnce();
            Expect.Call(mockEvent.Description).Return(testDesc).Repeat.AtLeastOnce();
            Expect.Call(mockEvent.StartTime).Return(testStart).Repeat.AtLeastOnce();
            Expect.Call(mockEvent.EndTime).Return(testEnd).Repeat.AtLeastOnce();

            mockEvent.Replay();

            // expand the occurances
            MonthlyRecurrencePattern testPattern = new MonthlyRecurrencePattern(testRecurrenceEnd, 1);

            DateTime expandWindowBegin = DateTime.Parse("4/1/2001");
            DateTime expandWindowEnd = DateTime.Parse("6/1/2005");

            // make sure that the recurrence got computed correctly
            var expandedEvents = testPattern.Expand(mockEvent, expandWindowBegin, expandWindowEnd);

            Assert.AreEqual(0, expandedEvents.Count);
        }

        [TestMethod]
        public void Expand_WithEventDurationLongerThanRecurrenceInterval_AndExpandWindowThatStartsAndEndsBeforeTheEventBegin_DoesNotExpand()
        {
            DateTime testStart = DateTime.Parse("1/1/2000 1:00 pm");
            DateTime testEnd = DateTime.Parse("2/3/2000 2:00 pm");
            string testDesc = "Test Recurring Event";
            string testName = "Test Name";

            DateTime testRecurrenceEnd = DateTime.Parse("11/9/2000");

            RecurrableEventDefinition mockEvent = mockMaker.StrictMock<RecurrableEventDefinition>();
            Expect.Call(mockEvent.Name).Return(testName).Repeat.AtLeastOnce();
            Expect.Call(mockEvent.Description).Return(testDesc).Repeat.AtLeastOnce();
            Expect.Call(mockEvent.StartTime).Return(testStart).Repeat.AtLeastOnce();
            Expect.Call(mockEvent.EndTime).Return(testEnd).Repeat.AtLeastOnce();

            mockEvent.Replay();

            // expand the occurances
            MonthlyRecurrencePattern testPattern = new MonthlyRecurrencePattern(testRecurrenceEnd, 1);

            DateTime expandWindowBegin = DateTime.Parse("2/1/1999");
            DateTime expandWindowEnd = DateTime.Parse("5/1/1999");

            // make sure that the recurrence got computed correctly
            var expandedEvents = testPattern.Expand(mockEvent, expandWindowBegin, expandWindowEnd);

            Assert.AreEqual(0, expandedEvents.Count);
        }

        [TestMethod]
        public void ExpandRecurringEventWithDeletionException()
        {
            // establish values for a IEventDefinition stand-in
            string testName = "Test Monthly Recurring Event";
            string testDesc = "Event used for unit testing monthly recurring events during a window beginning during " +
                "the period of event occurance and ending during the period of event occurance";
            DateTime testStart = DateTime.Parse("1/1/2000 1:00 pm");  // when the event starts
            DateTime testEnd = DateTime.Parse("1/1/2000 5:00 pm");  // when the event ends
            DateTime expandWindowBegin = DateTime.Parse("1/1/2000"); // we will start expanding recurrences from
            DateTime expandWindowEnd = DateTime.Parse("6/15/2000"); // we will stop expanding recurrences on
            DateTime testRecurrenceEnd = DateTime.Parse("5/10/2000");
            DateTime dateToRemove = DateTime.Parse("3/1/2000");
            int monthlyRecurrenceInterval = 1;

            // set up the mock object using the mocking framework
            RecurrableEventDefinition mockEvent = mockMaker.StrictMock<RecurrableEventDefinition>();
            Expect.Call(mockEvent.Name).Return(testName).Repeat.AtLeastOnce();
            Expect.Call(mockEvent.Description).Return(testDesc).Repeat.AtLeastOnce();
            Expect.Call(mockEvent.StartTime).Return(testStart).Repeat.AtLeastOnce();
            Expect.Call(mockEvent.EndTime).Return(testEnd).Repeat.AtLeastOnce();

            mockEvent.Replay();

            MonthlyRecurrencePattern testPattern = new MonthlyRecurrencePattern(testRecurrenceEnd, monthlyRecurrenceInterval);
           

            // create an exception that omits one of the occurances
            PatternDeviation exception = testPattern.RemoveInstance(dateToRemove);

            // expand the event occurances out
            var expandedEvents = testPattern.Expand(mockEvent, expandWindowBegin, expandWindowEnd);

            // make sure that the recurrence got computed correctly
            Assert.AreEqual(4, expandedEvents.Count);

            var numUnremovedExceptionInstances = (from i in expandedEvents where i.StartTime == dateToRemove select i).Count<IEventInstance>();

            Assert.AreEqual(0, numUnremovedExceptionInstances);
        }

        [TestMethod]
        public void ExpandRecurringEventWithModificationException()
        {
            // establish values for a IEventDefinition stand-in
            string testName = "Test Monthly Recurring Event";
            string testDesc = "Event used for unit testing monthly recurring events during a window beginning during " +
                "the period of event occurance and ending during the period of event occurance";
            DateTime testStart = DateTime.Parse("1/1/2000 1:00 pm");  // when the event starts
            DateTime testEnd = DateTime.Parse("1/1/2000 5:00 pm");  // when the event ends
            DateTime expandWindowBegin = DateTime.Parse("1/1/2000"); // we will start expanding recurrences from
            DateTime expandWindowEnd = DateTime.Parse("6/15/2000"); // we will stop expanding recurrences on
            DateTime testRecurrenceEnd = DateTime.Parse("10/1/2000");
            int monthlyRecurrenceInterval = 1;

            DateTime dateToModify = DateTime.Parse("5/1/2000");
            DateTime modifiedStart = DateTime.Parse("1/15/2000 6:00:00 pm");
            DateTime modifiedEnd = DateTime.Parse("1/15/2000 7:00:00 pm");

            // set up the mock object using the mocking framework
            RecurrableEventDefinition mockEvent = mockMaker.StrictMock<RecurrableEventDefinition>();
            Expect.Call(mockEvent.Name).Return(testName).Repeat.AtLeastOnce();
            Expect.Call(mockEvent.Description).Return(testDesc).Repeat.AtLeastOnce();
            Expect.Call(mockEvent.StartTime).Return(testStart).Repeat.AtLeastOnce();
            Expect.Call(mockEvent.EndTime).Return(testEnd).Repeat.AtLeastOnce();

            mockEvent.Replay();

            MonthlyRecurrencePattern testPattern = new MonthlyRecurrencePattern(testRecurrenceEnd, monthlyRecurrenceInterval);

            // create an exception that alters one of the occurances
            testPattern.AlterInstance(DateTime.Parse("5/1/2000"),
                                "modified event", "modified event!",
                                DateTime.Parse("1/15/2000 6:00:00 pm"),
                                DateTime.Parse("1/15/2000 7:00:00 pm"));

            // expand the occurances
            var eventList = testPattern.Expand(mockEvent, DateTime.Parse("1/1/2000"), DateTime.Parse("10/20/2000"));

            // make sure that the recurrence got computed correctly
            Assert.AreEqual(10, eventList.Count);

            // make sure that our event exception got included
            var actualEventExceptions = (from i in eventList where i.StartTime == DateTime.Parse("1/15/2000 6:00:00 pm") 
                                               && i.EndTime == DateTime.Parse("1/15/2000 7:00:00 pm") select i).Count<IEventInstance>();
            
            Assert.AreEqual(1, actualEventExceptions);
        }
    }
}